﻿using KinonekoSoftware.Studio.Pages.Galleries;
using KinonekoSoftware.Studio.Pages.Shenanigans;
using KinonekoSoftware.Studio.Utils.Transformers;
using LiteDB;

namespace KinonekoSoftware.Studio.ViewModels
{
    partial class AppViewModel
    {


        #region OnDatabaseOpeningImpl / OnDatabaseClosingImpl

        private void OnDatabaseClosingImpl(IRepository repository, string dir)
        {

            Xplat.RunOnUIThread(() =>
            {
                //
                //
                OnDatabaseClosing(repository, dir);
            });
        }

        private void OnDatabaseOpeningImpl(
            IRepository                                 repository,
            string                                      dir,
            IReadOnlyCollection<IRepositoryInitializer> initializers,
            IReadOnlyCollection<IRepositoryMaintainer>  maintainers,
            bool                                        upgrade)
        {
            Xplat.RunOnUIThread(() =>
            {
                try
                {

                    //
                    //
                    var vm = Ioc.Get<AppViewModel>();
                    var iterator = vm.Inboard
                                     .Concat(vm.Outboard)
                                     .OfType<RepositoryCreationViewModel>();
                    var exists = iterator.FirstOrDefault();

                    //
                    // 在加载世界观的时候，检查是否有未关闭的创建世界观页面。
                    if (exists is not null)
                    {
                        Close(exists);
                    }

                    OnDatabaseOpening(repository, dir, initializers, maintainers, upgrade);
                }
                catch(Exception ex)
                {
                    return;
                }
            });
        }

        #endregion

        internal async Task Snapshot(IRepository repository)
        {
            var snapshot     = repository.Snapshot();
            var repo         = Ioc.Get<RepositorySetting>();
            var ai           = Ioc.Get<ApplicationInformation>();
            var repoFileName = Path.Combine(ai.SettingFolder, Platform.FileName_Setting_Repo);
            var inside       = repo.Repositories.FirstOrDefault(x => x.Path == snapshot.Path);
            var inside2      = Repositories.FirstOrDefault(x => x.Path      == snapshot.Path);

            if (inside is null ||
                inside2 is null)
            {
                repo.Repositories.Add(snapshot);
                Repositories.Add(snapshot);
            }
            else
            {
                inside2.Name        = inside.Name        = snapshot.Name;
                inside2.Intro       = inside.Intro       = snapshot.Intro;
                inside2.Author      = inside.Author      = snapshot.Author;
                inside2.ForeignName = inside.ForeignName = snapshot.ForeignName;
            }

            await JSON.DumpAsync(repo, repoFileName, false);
        }


        private void OnDatabaseClosing(IRepository repository, string dir)
        {
            if (repository is null)
            {
                return;
            }


            //
            // 关闭当前
            Platform.Close();

            //
            // 清空资源
            FinalClosing();

            //
            //
            LaunchContext.IsLoaded = false;

            //
            // 清空资源
            CloseAll();

            //
            //
            Xplat.Debug($"正在切换世界观，上个世界观为：{dir}");
        }


        private async void OnDatabaseOpening(
            IRepository                                 repository,
            string                                      dir,
            IReadOnlyCollection<IRepositoryInitializer> initializers,
            IReadOnlyCollection<IRepositoryMaintainer>  maintainers,
            bool                                        upgrade)
        {
            var snapshot = repository.Snapshot();
            PrepareProtectedMode(repository.Mode);

            //
            //
            var repo         = Ioc.Get<RepositorySetting>();
            var ai           = Ioc.Get<ApplicationInformation>();
            var repoFileName = Path.Combine(ai.SettingFolder, Platform.FileName_Setting_Repo);

            //
            // 如果打开失败，重新打开时设置最后一次打开的世界观
            if (string.IsNullOrEmpty(repo.LastRepository))
            {
                repo.LastRepository = dir;
                await JSON.DumpAsync(repo, repoFileName, false);
            }

            //
            // 添加世界观
            if (RepositoryHashSet.Add(dir))
            {
                Repositories.Add(snapshot);
                repo.LastRepository = dir;
                repo.Repositories.Add(snapshot);
                await JSON.DumpAsync(repo, repoFileName, false);
            }
            else
            {
                repo.LastRepository = dir;
                await JSON.DumpAsync(repo, repoFileName, false);
            }


            //
            //

            LaunchContext.CurrentRepository = snapshot;
            LaunchContext.IsLoaded          = true;


            //
            //
            await PrepareDatabaseInitialize(dir, repository, initializers, maintainers, upgrade);


            FinalLoading();
        }


        private void PrepareProtectedMode(DataMode mode)
        {
            if (mode == DataMode.Protected)
            {
                //
                // 添加相同实例
                var dump     = new DatabaseDumpNotification { MessageBus       = _notifyQueue };
                var switcher = new DatabaseModeSwitchNotification { MessageBus = _notifyQueue };

                //
                //
                Notifications.Add(dump);
                Notifications.Add(switcher);
            }

            var backup = new DatabaseBackupNotification { MessageBus = _notifyQueue };
            Notifications.Add(backup);
        }

        private async Task PrepareDatabaseInitialize(
            string                                      dir,
            IRepository                                 repository,
            IReadOnlyCollection<IRepositoryInitializer> initializers,
            IReadOnlyCollection<IRepositoryMaintainer>  maintainers,
            bool                                        upgrade)
        {
            var result = false;

            //
            // 维护世界观
            using (var session = await BeginSession(SR.Title_Repository_Loading))
            {
                await session.Await();

                if (upgrade)
                {
                    session.Update(SR.Title_Repository_Migrating);
                    await session.Await();

                    MigrateDatabase(dir, (Repository)repository);
                }

                if (initializers.Count != 0)
                {
                    session.Update(SR.Title_Repository_Initializing);
                    await session.Await();

                    //
                    //
                    foreach (var initializer in initializers)
                    {
                        initializer.Initialize(repository);
                        result = true;
                    }
                }

                if (maintainers.Count != 0)
                {
                    session.Update(SR.Title_Repository_Maintaining);
                    await session.Await();

                    //
                    //
                    foreach (var maintainer in maintainers)
                    {
                        maintainer.Maintain(repository);
                        result = true;
                    }
                }

                await session.Await();
            }


            if (result)
            {
                await Info(TitleSR.SuccessOperation, "升级世界观完成，请重新启动橘小柚！");
            }
        }

        private static void MigrateDatabase(string dir, Repository repository)
        {
            using (var oldRepo = new LiteDatabase(@$"{dir}\main.mgdb"))
            {
                try
                {
                    var context = new TransformContext
                    {
                        OldRepoDir     = dir,
                        NewRepoDir     = dir,
                        OldRepo        = oldRepo,
                        NewRepo        = repository.Kernel,
                        Repository     = repository,
                        DocumentMapper = new Dictionary<string, string>(),
                    };

                    foreach (var transformer in Transformer.Transformers)
                    {
                        transformer.Migrate(context);
                    }
                }
                catch(Exception ex)
                {
                    Xplat.Warning(ex.Message);
                }
            }
        }

        private void FinalClosing()
        {
            //
            // 组件
            // Ioc.Get<IGlossaryComponent>().Stop();
            // Ioc.Get<ITimelineComponent>().Stop();

            //
            //
            GalleryFactory.Clear();
            Notifications.Clear();
        }

        private void FinalLoading()
        {
            //
            // 通知组件更新
            // Ioc.Get<IGlossaryComponent>().Start();
            // Ioc.Get<ITimelineComponent>().Start();

            //
            //
            Platform.Start();

            //
            //
            RefreshLoadState();

            //
            //
            var shell    = (TabbedShell)Ioc.Get<ShellCore>();
            var creation = shell.Inboard.Concat(shell.Outboard).OfType<RepositoryCreationViewModel>().FirstOrDefault();

            if (creation is not null)
            {
                shell.Close(creation);
            }
        }
    }
}